#include "UDPBasicApp.h"
#include <omnetpp.h>
#include "UDPControlInfo_m.h"
#include "IPvXAddressResolver.h"
#include "InterfaceTableAccess.h"

class VoIPApp : public UDPBasicApp
{
private:
    double delay;
    double sumdelay;
    cOutVector delayVector;
    cDoubleHistogram delayStats;
protected:
    virtual void initialize(int stage);
    virtual void sendPacket();
    virtual void processPacket(cPacket *msg) ;
    virtual void finish();
};

Define_Module(VoIPApp);

//Initialize numFailure with 0   &   WATCH numFailure
void VoIPApp::initialize(int stage)
{
    // because of IPvXAddressResolver, we need to wait until interfaces are registered,
    // address auto-assignment takes place etc.
    if (stage != 3)
        return;

    counter = 0;
    sumdelay = 0;
    numSent = 0;
    numReceived = 0;
    WATCH(sumdelay);
    WATCH(numSent);
    WATCH(numReceived);

    sentPkSignal = registerSignal("sentPk");
    rcvdPkSignal = registerSignal("rcvdPk");

    localPort = par("localPort");
    destPort = par("destPort");

    const char *destAddrs = par("destAddresses");
    cStringTokenizer tokenizer(destAddrs);
    const char *token;

    while ((token = tokenizer.nextToken()) != NULL)
        destAddresses.push_back(IPvXAddressResolver().resolve(token));

    socket.setOutputGate(gate("udpOut"));
    socket.bind(localPort);
    setSocketOptions();

    if (destAddresses.empty())
        return;

    stopTime = par("stopTime").doubleValue();
    simtime_t startTime = par("startTime").doubleValue();

    if (stopTime != 0 && stopTime <= startTime)
        error("Invalid startTime/stopTime parameters");
    cMessage *timerMsg = new cMessage("sendTimer");
    scheduleAt(startTime, timerMsg);
}

//add warmup-period to numSent increment
void VoIPApp::sendPacket()
{
    cPacket *payload = createPacket();
    IPvXAddress destAddr = chooseDestAddr();
    emit(sentPkSignal, payload);
    socket.sendTo(payload, destAddr, destPort);
    if (simTime() >= simulation.getWarmupPeriod())
    {
        numSent++;
    }
}

//increase numFailure if packet arrives too late
void VoIPApp::processPacket(cPacket *pk)
{
    emit(rcvdPkSignal, pk);
    EV << "Received packet: " << UDPSocket::getReceivedPacketInfo(pk) << endl;

    delay = simTime().dbl() - pk->getCreationTime().dbl();
    sumdelay += delay;
    delayVector.record(delay);
    delayStats.collect(delay);

    if (simTime() >= simulation.getWarmupPeriod())
    {
        if (delay <= 0.1)
            numReceived++;//numFailure++;
    }
    delete pk;
}
//record numFailure
void VoIPApp::finish()
{
    sumdelay = sumdelay/numSent;
    recordScalar("delay average", sumdelay);
    recordScalar("packets sent", numSent);
    recordScalar("packets received", numReceived);

    delayStats.recordAs("delay stats");
}
